﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using YmtAuth.AppService.Safe;
using YmtAuth.Common.Extend;
using YmtAuth.Domain.DomainService;
using YmtAuth.Domain.EventHistory;
using YmtAuth.Domain.Model.BlacklistUser;
using YmtAuth.Domain.Repository;
using YmtAuth.Repository.MSSQL;
using YmtSystem.CrossCutting;
using YmtAuth.Domain.Model.Safe;

namespace YmtAuth.AppService
{
    /// <summary>
    /// 黑名单
    /// </summary>
    public class BlackListAppService
    {       
        private static readonly IBlackListRepository blackRepo = LocalServiceLocator.GetService<IBlackListRepository>();
        //检查IP是否被禁止访问
        public static bool TryCheckIpIsDisableRequest(string ip, string clientTypSource = null)
        {
            try
            {
                var enableStr = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                    .TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("Open"),"1");
                if (enableStr.IsEmpty() || enableStr == "0")
                    return false;
                InitIpMatchpec();               
                var ipInfo = new BlackList(ip);
                var isWhitelist = Action_Extend.ActionWatch(() => ipInfo.CheckRequestIpIsWhitelist(ip), "[CheckRequestIpIsWhitelist]");
                if (isWhitelist)
                {
                    return false;
                }
                var _ip = Action_Extend.ActionWatch(() => blackRepo.FindOne(YmatouBlacklistUserSpecifications.MathRequestIP(ip)
                    , YmatouBlacklistUserSpecifications.GetBlacklistDbName()
                    , YmatouBlacklistUserSpecifications.GetBlacklistTableName(clientTypSource))
                    , "BlackListRepository.FindOne");
                if (_ip != null)
                {
                    return _ip.IsDisableRequest();
                }
                return false;
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error(string.Format("检查黑名单IP异常,ip:{0},clientTypSource:{1}", ip, clientTypSource), ex);
                return false;
            }
        }
        //添加IP黑名单
        public static void TryAddBlackListIpAsync(string source, string ip, string loginId, bool loginSuccess)
        {
            if (YmatouBlacklistUserSpecifications.IsSellerSource(source))
                TryAddSellerBlackListIpAsync(source, ip, loginId, loginSuccess);
            else
                TryAddBuyerBlackListIpAsync(source, ip, loginId, loginSuccess);
        }

        //添加买手IP黑名单
        public static void TryAddSellerBlackListIpAsync(string source, string ip, string loginId, bool loginSuccess)
        {
            try
            {
                var cfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration();
                //登录成功次数：如，10 次
                var successlimitStr =
                    cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("SellerLoginSuccessLimit"),"30");
                //登录失败次数：如，20 次
                var faillimitStr = cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("SellerLoginFailLimit"),"20");
                if ((successlimitStr.IsEmpty() && faillimitStr.IsEmpty())
                    || (successlimitStr == "0" && faillimitStr == "0"))
                    return;
                InitIpMatchpec();
               
                //x分钟内登录成功
                var successLimitMinutes =
                    cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("SellerLoginSuccessLimitMinutes"),"-3");
                //x分钟内登录失败
                var failLimitMinutes =
                    cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("SellerLoginFailLimitMinutes"), "-3");
                /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                var ipInfo = new BlackList(ip);
                //如果是白名单IP，则退出
                if (ipInfo.CheckRequestIpIsWhitelist(ip))
                {
                    if (ipInfo.IpWhitelistStoreDb(
                        cfg[SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("WhitelistStoreDb")]))
                    {
                        ipInfo.SetWhitelistIp();
                        StoreWhitelistIp(ip, ipInfo, YmatouBlacklistUserSpecifications.Seller_Type);
                    }
                    return;
                }
                var task = loginSuccess
                    ? Task.Factory.StartNew(
                        () => CountLoginSuccessIp(ip, loginId, successLimitMinutes, successlimitStr, YmatouBlacklistUserSpecifications.Seller_Type))
                    : Task.Factory.StartNew(() => CountLoginFailIp(ip, loginId, failLimitMinutes, faillimitStr, YmatouBlacklistUserSpecifications.Seller_Type));
                task.ContinueWith(t1 =>
                {
                    if (t1.Exception != null)
                    {
                        YmatouLoggingService.Error("Add Seller blackIp Exception {0},{1}", ip,
                            t1.Exception.InnerException);
                    }
                    else
                    {
                        if (t1.Result)
                        {
                            //锁定超时时间：如 00:05:00
                            var lockExpired =
                                cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("SellerIpLockExpired"),
                                    "00:30:00");                            
                            SaveBlackIpInfo(ip, loginId, ipInfo, lockExpired, YmatouBlacklistUserSpecifications.Seller_Type);
                        }
                    }
                }, TaskContinuationOptions.OnlyOnRanToCompletion);
            }
            catch (AggregateException ex)
            {
                YmatouLoggingService.Error("Add Seller BlackIp AggregateException.2{0},{1}", ip, ex.InnerException);
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("Add Seller BlackIp exception.3 {0},{1}", ip, ex);
            }
        }

        //添加买家IP黑名单
        public static void TryAddBuyerBlackListIpAsync(string source,string ip,string loginId, bool loginSuccess)
        {
            try
            {                        
                var cfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration();
                var successlimtiCountStr = cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("LoginSuccessLimit"), "50");
                var faillimtiCountStr = cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("LoginFailLimit"),"30");
                if ((successlimtiCountStr.IsEmpty() && faillimtiCountStr.IsEmpty())
                    || (successlimtiCountStr == "0" && faillimtiCountStr == "0"))
                    return;
                InitIpMatchpec();
                //var exFailTimeStr = cfg[SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("RequestFailIpLockExpired")];
                var exSuccessTimeStr =
                    cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("RequestSuccessIpLockExpired"), "00:30:00");
                var successlimitTimeSpanStr =
                    cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("LoginSuccessLimitMinutes"),"-5");
                var failLimitTimeSpanStr =
                    cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("LoginFailLimitMinutes"), "-5");

                var ipInfo = new BlackList(ip);
                //如果是白名单IP，则退出
                if (ipInfo.CheckRequestIpIsWhitelist(ip))
                {
                    if (ipInfo.IpWhitelistStoreDb(
                            cfg.TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("WhitelistStoreDb"),"1")))
                    {
                        ipInfo.SetWhitelistIp();
                        StoreWhitelistIp(ip, ipInfo, YmatouBlacklistUserSpecifications.Buyer_Type);
                    }
                    return;                    
                }               
                var task = loginSuccess ?Task.Factory.StartNew(() => CountLoginSuccessIp(ip, loginId, successlimitTimeSpanStr, successlimtiCountStr))
                    :Task.Factory.StartNew(() => CountLoginFailIp(ip, loginId, failLimitTimeSpanStr, faillimtiCountStr));
                task.ContinueWith(t1 =>
                {
                    if (t1.Exception != null)
                    {
                        YmatouLoggingService.Error("添加黑名单异常1(AggregateException) {0},{1}", ip,
                            t1.Exception.InnerException);
                    }
                    else
                    {
                        if (t1.Result)
                        {
                            SaveBlackIpInfo(ip, loginId, ipInfo, exSuccessTimeStr, YmatouBlacklistUserSpecifications.Buyer_Type);
                        }
                    }
                }, TaskContinuationOptions.OnlyOnRanToCompletion);               
            }
            catch (AggregateException ex)
            {
                YmatouLoggingService.Error("添加黑名单异常2(AggregateException) {0},{1}", ip, ex.InnerException);
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("添加黑名单异常3 {0},{1}" ,ip, ex);
            }
        }
        //保存黑名单
        private static void SaveBlackIpInfo(string ip, string loginId, BlackList ipInfo, string exSuccessTimeStr,string userType)
        {
            var db = YmatouBlacklistUserSpecifications.GetBlacklistDbName();
            var tableName = YmatouBlacklistUserSpecifications.GetBlacklistTableName(userType);           
            var _ipInfo = blackRepo.FindOne(YmatouBlacklistUserSpecifications.MathRequestIP(ip),db,tableName);
            if (_ipInfo == null)
            {
                ipInfo.SetBlackListIp(TimeSpan.Parse(exSuccessTimeStr));
                blackRepo.Save(ipInfo, WriteConcern.WMajority,db,tableName);
            }
            else
            {
                if (_ipInfo.CheckLockedTimeIsExpired())
                    blackRepo.Update(YmatouBlacklistUserSpecifications.MathRequestIP(ip),
                        YmatouBlacklistUserSpecifications.IpUpdate(
                            TimeSpan.Parse(exSuccessTimeStr)), WriteConcern.Acknowledged,db,tableName);
            }
            YmatouLoggingService.Debug("用户 {0},IP {1} 加入黑名单", loginId, ip);
        }
      
        //登录失败IP计数
        private static bool CountLoginFailIp(string ip, string loginId, string failLimitTimeSpanStr, string faillimtiCountStr,string userType="buyer")
        {
            var result = RepositoryDependencies.UserLoginIpCountRepository.Invoke()
                .CheckIsNeedCountIp(ip, loginId, Math.Abs(Convert.ToInt32(failLimitTimeSpanStr)), false, userType);
            YmatouLoggingService.Debug("添加登录失败IP计数 [CountLoginFailIp], loginId: {0}, ip: {1}, userType: {2}, IsNeedCountIp: {3}", loginId, ip, userType, result);
            if (result > 0)
            {
                var countResult = RepositoryDependencies.UserLoginIpCountRepository.Invoke()
                    .AddUserLoginIpCount(ip, Math.Abs(Convert.ToInt32(failLimitTimeSpanStr)), false, userType);
                YmatouLoggingService.Debug("[CountLoginFailIp], Add, return: {0}", countResult);
            }
            var loginFailCount = RepositoryDependencies.UserLoginIpCountRepository.Invoke()
                .GetUserLoginIpCount(ip,
                    -Math.Abs(Convert.ToInt32(failLimitTimeSpanStr)),
                    userType, false);
            return loginFailCount >= Convert.ToInt32(faillimtiCountStr);
        }
        //登录成功IP计数
        private static bool CountLoginSuccessIp(string ip, string loginId, string successLimitTimeSpanStr,
            string successLimtiCountStr,string userType="buyer")
        {
            var result = RepositoryDependencies.UserLoginIpCountRepository.Invoke()
               .CheckIsNeedCountIp(ip, loginId, Math.Abs(Convert.ToInt32(successLimitTimeSpanStr)), true,userType);

            YmatouLoggingService.Debug("添加登录成功IP计数 [CountLoginSuccessIp], loginId: {0}, ip: {1}, userType: {2}, IsNeedCountIp: {3}", loginId, ip, userType, result);

            if (result > 0)
            {
                var countResult = RepositoryDependencies.UserLoginIpCountRepository.Invoke()
                    .AddUserLoginIpCount(ip, Math.Abs(Convert.ToInt32(successLimitTimeSpanStr)), true,userType);
                YmatouLoggingService.Debug(" [CountLoginSuccessIp], Add, return: {0}", countResult);
            }
            var loginSuccessCount = RepositoryDependencies.UserLoginIpCountRepository.Invoke()
                .GetUserLoginIpCount(ip,
                    -Math.Abs(Convert.ToInt32(successLimitTimeSpanStr)),userType, true);
            return loginSuccessCount >= Convert.ToInt32(successLimtiCountStr);
        }
        //锁定Ip
        public static void LockIp(string[] ipList, TimeSpan lockdTimeOut)
        {
            var list = new List<BlackList>();
            ipList.Each(ip =>
            {
                var ipInfo = new BlackList(ip);
                ipInfo.SetBlackListIp(lockdTimeOut);
                list.Add(ipInfo);
                blackRepo.Save(ipInfo);
            });
            //requestIpInfoRepo.AddRang(list);
        }
        public static void UnLockIp(string[] ipList, int userType)
        {
            var db = YmatouBlacklistUserSpecifications.GetBlacklistDbName();

            string userTypeStr = userType == 1 ? YmatouBlacklistUserSpecifications.Buyer_Type : YmatouBlacklistUserSpecifications.Seller_Type;
            var table = YmatouBlacklistUserSpecifications.GetBlacklistTableName(userTypeStr);
            if (ipList != null && ipList.Any())
            {
                blackRepo.Remove(YmatouBlacklistUserSpecifications.MatchIpListIN(ipList), db, table);
            }            
        }
        public static IEnumerable<BlackList> FindRequestIPList(string[] ipList)
        {
            return blackRepo.Find(YmatouBlacklistUserSpecifications.MatchIpListIN(ipList)).ToList();
        }

        /// <summary>
        /// 添加用户 设备/Client 黑名单
        /// </summary>
        /// <param name="source"></param>
        /// <param name="device"></param>
        /// <param name="clientId"></param>
        /// <param name="loginHistoryRepo"></param>
        public static void TryAddBlackListDeviceAsync(string source, string device,string clientId,
            IYmtUserLoginHistoryRepository loginHistoryRepo)
        {
            bool isGTLimit = false;

            //只有app端才有设备号
            //if (source.IsEmpty() || source.ToLower() != "app") return;
            try
            {
                string key = string.Empty;
                byte deviceType = UserDeviceAuthEntity.DeviceIdType;
                if (device.IsEmpty())
                {
                    key = clientId;
                    deviceType = UserDeviceAuthEntity.ClientIdType;
                }
                else
                {
                    key = device;
                }
                
                if (key.IsEmpty())
                {
                    YmatouLoggingService.Debug("设备号 ，client ID 都为空无法添加黑名单");
                    return;
                }
                
                var loginSuccessTask = Task.Factory.StartNew(() =>
                {
                    var successCountCfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                        .TryGet(SafeVerifyConfiguration.BlackListCfg.DeviceBlackListKey("DeviceLoginSuccessLimit"));
                    if (successCountCfg.IsEmpty() || successCountCfg == "0") 
                        return false;
                    var successTimeCfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                        .TryGet(SafeVerifyConfiguration.BlackListCfg.DeviceBlackListKey("DeviceLoginSuccessTime"));
                    int loginDeviceSuccessCount = loginHistoryRepo.GetLoginDeviceCount(deviceType, key, true, successTimeCfg.ConvertToInt32(1));

                    isGTLimit = loginDeviceSuccessCount >= successCountCfg.ConvertToInt32(20);

                    YmatouLoggingService.Debug("设备 {0} 登录成功超限? {1},配置值为 {2}", key, isGTLimit, successCountCfg);
                    return isGTLimit;
                });
                var loginFailTask = Task.Factory.StartNew(() =>
                {
                    var failCountCfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                        .TryGet(SafeVerifyConfiguration.BlackListCfg.DeviceBlackListKey("DeviceLoginFailLimit"));
                    if (failCountCfg.IsEmpty() || failCountCfg == "0")
                        return false;
                    var failTimeCfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                        .TryGet(SafeVerifyConfiguration.BlackListCfg.DeviceBlackListKey("DeviceLoginFailTime"));

                    int loginDeviceFailCount = loginHistoryRepo.GetLoginDeviceCount(deviceType, key, false, failTimeCfg.ConvertToInt32(1));

                    isGTLimit = loginDeviceFailCount >= failCountCfg.ConvertToInt32(20);

                    YmatouLoggingService.Debug("设备 {0} 登录失败超限? {1},配置值为 {2}", key, isGTLimit, failCountCfg);
                    return isGTLimit;
                });
                Task.WhenAll(new Task<bool>[] {loginSuccessTask, loginFailTask}).ContinueWith(t =>
                {
                    if (t.Result.Any(b => b == true))
                    {
                        var lockTimeOut =
                            SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                                .TryGet(
                                    SafeVerifyConfiguration.BlackListCfg.DeviceBlackListKey("DeviceBlackListLockTimeOut"));
                       
                        var blackListDevice = blackRepo.FindOne(YmatouBlacklistUserSpecifications.MatchDeviceId(key),
                            true);
                        if (blackListDevice == null)
                        {
                            blackListDevice = new BlackList(device, 1);
                        }
                        blackListDevice.SetBlackListDeviceIdLockdTimeOut(TimeSpan.Parse(lockTimeOut));
                        blackRepo.Save(blackListDevice, WriteConcern.WMajority);
                        YmatouLoggingService.Debug("设备号{0}加入黑名单", device);
                    }
                }).ContinueWith(t => { if (t.IsFaulted)
                    if (t.Exception != null) YmatouLoggingService.Error("添加设备号黑名单异常 {0}", t.Exception.InnerExceptions);
                },
                    TaskContinuationOptions.OnlyOnFaulted);
            }
            catch (AggregateException ex)
            {
                YmatouLoggingService.Error("添加设备黑名单异常2(AggregateException) {0},{1}", device, ex.InnerException);
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("添加设备黑名单异常3(AggregateException) {0},{1}", device, ex);
            }
        }

        /// <summary>
        /// 初始化IP匹配规则
        /// </summary>
        public static void InitIpMatchpec()
        {
            try
            {               
                var isCache = false;
                var cfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration(out isCache);
                //如果不是从缓存中获取配置，则刷新白名单配置
                if (!isCache)
                {
                    Action_Extend.ActionWatch(
                        () =>
                            IpMatchSpec.SetIpMatchSpec(
                                cfg[SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("Whitelist")]
                                , cfg[SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("WhitelistAllMatch")]),
                        "[InitIpMatchBulackSpec] SetIpMatchSpec");
                }
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("初始化IP匹配规则失败", ex);
            }          
        }
       
        //保存IP白名单
        private static void StoreWhitelistIp(string ip, BlackList ipInfo,string userType)
        {
            var db = YmatouBlacklistUserSpecifications.GetBlacklistDbName();
            var table = YmatouBlacklistUserSpecifications.GetBlacklistTableName(userType);           
            var _ip = blackRepo.FindOne(YmatouBlacklistUserSpecifications.MathRequestIP(ip),db,table);
            if (_ip == null)
                blackRepo.Save(ipInfo,db,table);
        }
    }
}
